<?php

namespace magein\think\command;

use magein\utils\File;
use magein\utils\Variable;
use magein\think\command\traits\CommandParamParse;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument as InputArgument;
use think\console\Output;
use think\Model;

class MakeModelAttr extends Command
{

    use CommandParamParse;

    protected string $help = "
    php think model:attr             识别Models/*.php文件
    php think model:attr user        识别Models/User.php文件
    php think model:attr user/order  识别Models/User/Order.php文件
    php think model:attr user/order_good  识别Models/User/OrderGood.php文件
";

    protected function configure()
    {
        // 指令配置
        $this->setName('model:attr')
            ->addArgument('filename', InputArgument::OPTIONAL, '文件名称')
            ->setDescription('生成模型的属性')
            ->setHelp($this->help);
    }

    protected function execute(Input $input, Output $output)
    {
        if (\think\facade\Env::get('app_env') !== 'local') {
            $output->error('只能在开发环境中使用');
            exit();
        }

        // 指令输出
        $name = $input->getArgument('filename');

        $filepath = $this->app->config->get('console.config.model.path', '') ?: 'app/model';
        $filepath = trim($filepath, '/') . '/';
        $files = [];
        if ($name) {

            $names = explode('/', $name);
            $filename = array_pop($names);

            foreach ($names as $item) {
                $filepath .= Variable::camelCase($item) . '/';
            }

            $filepath .= Variable::pascal(trim($filename, '/')) . '.php';
            if (is_file($filepath)) {
                $files[] = $filepath;
            }
        } else {
            $result = File::getTreeList(trim($filepath, '/'), ['php']);
            $files = $result->getData();
        }

        if (empty($files)) {
            $output->error($filepath . '目录下没有模型文件');
            $output->error($this->help);
            exit();
        }

        foreach ($files as $path) {
            $content = file_get_contents($path);
            $namespace = preg_replace(['/\.\/app/', '/\.php/', '/\//'], ['app', '', '\\\\'], $path);

            if (preg_match('/\* @property/', $content)) {
                $output->comment('continue file : ' . $path);
                continue;
            }

            /**
             * @var Model $model
             */
            $model = new $namespace();
            $property = $this->make($model->getName());
            if (empty($property)) {
                continue;
            }

            // 替换属性
            $filename = pathinfo($path, PATHINFO_FILENAME);
            $content = preg_replace("/class $filename/", $property . "\n" . "class $filename", $content);
            file_put_contents($path, $content);
            $output->writeln('success file: ' . $path);
        }
    }

    /**
     * 生成模型属性
     * 支持外部调用
     * @param string $table_name 模型实例
     * @return string
     */
    public function make(string $table_name): string
    {
        $result = $this->tableAttrs($table_name);
        if ($result->getCode()) {
            return '';
        }

        $attrs = $result->getData()['attrs'];

        $property = '/**';
        $property .= "\n";

        $cla_name = class_basename($table_name);
        $methods = "";
        $method_params = function ($prefix, $name, $param) {
            if ($prefix == '__') {
                $name = '\think\paginator\driver\Bootstrap';
            } elseif ($prefix == '___') {
                $name = '\think\model\Collection';
            } else {
                $name = Variable::pascal($name);
            }
            return ' * @method static ' . $name . '|null ' . $prefix . Variable::camelCase($param) . '($' . $param . ');' . "\n";
        };

        foreach ($attrs as $attr) {
            $field = $attr['Field'];
            $type = $attr['Type'];
            $key = $attr['Key'];
            if (in_array($field, ['id', 'created_at', 'updated_at', 'deleted_at'])) {
                continue;
            }
            if (str_contains($type, 'int')) {
                $type = 'integer';
            } elseif (str_contains($type, 'decimal')) {
                $type = 'float';
            } else {
                $type = 'string';
            }
            $property .= " * @property $type $" . $field;
            $property .= "\n";

            if (preg_match('/_id|_no|phone|email$/', $field) || $key) {
                $methods .= $method_params('_', $cla_name, $field);
                $methods .= $method_params('__', $cla_name, $field);
                $methods .= $method_params('___', $cla_name, $field);
            } elseif (str_starts_with($attr['Type'], 'char')) {
                $methods .= $method_params('_', $cla_name, $field);
                $methods .= $method_params('__', $cla_name, $field);
                $methods .= $method_params('___', $cla_name, $field);
            } elseif (str_contains($attr['Type'], 'tinyint')) {
                $methods .= $method_params('__', $cla_name, $field);
                $methods .= $method_params('___', $cla_name, $field);
            }
        }
        $property .= $methods;
        $property = trim($property, "\n");
        $property .= "\n */";

        return $property;
    }
}